/****************************************************************************
 Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.

 https://axmol.dev/

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

#include "UIListViewTest.h"

using namespace ax;
using namespace ax::ui;

const char* font_UIListViewTest = "fonts/Marker Felt.ttf";

UIListViewTests::UIListViewTests()
{
    ADD_TEST_CASE(UIListViewTest_Horizontal);
    ADD_TEST_CASE(UIListViewTest_Vertical);
    ADD_TEST_CASE(UIListViewTest_ScrollToItemVertical);
    ADD_TEST_CASE(UIListViewTest_ScrollToItemHorizontal);
    ADD_TEST_CASE(UIListViewTest_MagneticVertical);
    ADD_TEST_CASE(UIListViewTest_MagneticHorizontal);
    ADD_TEST_CASE(UIListViewTest_PaddingVertical);
    ADD_TEST_CASE(UIListViewTest_PaddingHorizontal);
    ADD_TEST_CASE(Issue12692);
    ADD_TEST_CASE(Issue8316);
}

// UIListViewTest_Vertical

UIListViewTest_Vertical::UIListViewTest_Vertical() {}

UIListViewTest_Vertical::~UIListViewTest_Vertical() {}

bool UIListViewTest_Vertical::init()
{
    if (UIScene::init())
    {
        Size widgetSize = _widget->getContentSize();

        _displayValueLabel =
            Text::create("There are 50 items, but we only create 5 templates", "fonts/Marker Felt.ttf", 20);
        _displayValueLabel->setAnchorPoint(Vec2(0.5f, -1.0f));
        _displayValueLabel->setPosition(
            Vec2(widgetSize.width / 2.0f,
                 widgetSize.height / 2.0f + _displayValueLabel->getContentSize().height * 1.5f + 30));
        _uiLayer->addChild(_displayValueLabel);

        Text* alert = Text::create("ListView vertical", "fonts/Marker Felt.ttf", 30);
        alert->setColor(Color32(159, 168, 176));
        alert->setPosition(
            Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f - alert->getContentSize().height * 3.075f));
        _uiLayer->addChild(alert);

        Layout* root = static_cast<Layout*>(_uiLayer->getChildByTag(81));

        Layout* background  = dynamic_cast<Layout*>(root->getChildByName("background_Panel"));
        Size backgroundSize = background->getContentSize();

        // create list view ex data

        for (int i = 0; i < _totalCount; ++i)
        {
            std::string ccstr = fmt::format("listview_item_{}", i);
            _array.emplace_back(ccstr);
        }

        // Create the list view ex
        _listView = ListView::create();
        // set list view ex direction
        _listView->setDirection(ui::ScrollView::Direction::VERTICAL);
        _listView->setBounceEnabled(true);
        _listView->setBackGroundImage("cocosui/green_edit.png");
        _listView->setGlobalZOrder(200);
        _listView->setBackGroundImageScale9Enabled(true);
        _listView->setContentSize(Size(240.0f, 130.0f));
        _listView->setPosition(Vec2((widgetSize - _listView->getContentSize()) / 2.0f));
        _listView->addEventListener(
            (ui::ListView::ccListViewCallback)AX_CALLBACK_2(UIListViewTest_Vertical::selectedItemEvent, this));
        _listView->addEventListener((ui::ListView::ccScrollViewCallback)AX_CALLBACK_2(
            UIListViewTest_Vertical::selectedItemEventScrollView, this));
        _listView->setScrollBarPositionFromCorner(Vec2(7, 7));
        _uiLayer->addChild(_listView);

        // create model
        Button* default_button = Button::create("cocosui/backtotoppressed.png", "cocosui/backtotopnormal.png");
        default_button->setName("Title Button");

        Layout* default_item = Layout::create();
        default_item->setTouchEnabled(true);
        default_item->setContentSize(default_button->getContentSize());
        default_button->setPosition(Vec2(default_item->getContentSize() / 2.0f));
        default_item->addChild(default_button);

        auto clippingNode = ClippingNode::create();
        auto sprite       = Sprite::create("cocosui/clippingHead.jpg");
        clippingNode->addChild(sprite);
        auto stencil = Sprite::create("cocosui/clippingStencil.jpg");
        clippingNode->setStencil(stencil);
        auto custom_item_contentSize = default_item->getContentSize();
        clippingNode->setPosition(sprite->getContentSize().width / 2, custom_item_contentSize.height / 2);
        clippingNode->setScale(0.5);
        default_item->addChild(clippingNode);

        // set model
        _listView->setItemModel(default_item);

        // set all items layout gravity
        _listView->setGravity(ListView::Gravity::CENTER_VERTICAL);

        // initial the data
        for (int i = 0; i < _totalCount; ++i)
        {
            if (i < _spawnCount)
            {
                Widget* item = default_item->clone();
                item->setTag(i);
                Button* btn = (Button*)item->getChildByName("Title Button");
                btn->setTitleText(_array.at(i));
                _listView->pushBackCustomItem(item);
            }
        }

        // set items margin
        float spacing = 4;
        _listView->setItemsMargin(spacing);
        _itemTemplateHeight    = default_item->getContentSize().height;
        this->_reuseItemOffset = (_itemTemplateHeight + spacing) * _spawnCount;

        // update listview data
        this->scheduleUpdate();

        _listView->forceDoLayout();
        float totalHeight = _itemTemplateHeight * _totalCount + (_totalCount - 1) * spacing;
        _listView->setInnerContainerSize(Size(_listView->getInnerContainerSize().width, totalHeight));
        _listView->jumpToTop();

        // Show the indexes of items on each boundary.
        {
            float position = 75;
            // Labels
            _indexLabels[0] = Text::create(" ", "fonts/Marker Felt.ttf", 12);
            _indexLabels[0]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
            _indexLabels[0]->setPosition(_uiLayer->getContentSize() / 2 + Size(0.0f, position));
            _uiLayer->addChild(_indexLabels[0]);
            _indexLabels[1] = Text::create("  ", "fonts/Marker Felt.ttf", 12);
            _indexLabels[1]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
            _indexLabels[1]->setPosition(_uiLayer->getContentSize() / 2 + Size(140.0f, 0.0f));
            _uiLayer->addChild(_indexLabels[1]);
            _indexLabels[2] = Text::create(" ", "fonts/Marker Felt.ttf", 12);
            _indexLabels[2]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
            _indexLabels[2]->setPosition(_uiLayer->getContentSize() / 2 + Size(0.0f, -position));
            _uiLayer->addChild(_indexLabels[2]);

            // Callback
            _listView->ScrollView::addEventListener([this](Object* ref, ScrollView::EventType eventType) {
                ListView* listView = dynamic_cast<ListView*>(ref);
                if (listView == nullptr || eventType != ScrollView::EventType::CONTAINER_MOVED)
                {
                    return;
                }
                auto bottom = listView->getBottommostItemInCurrentView();
                auto center = listView->getCenterItemInCurrentView();
                auto top    = listView->getTopmostItemInCurrentView();

                _indexLabels[0]->setString(fmt::format("Top index={}", listView->getIndex(top)));
                _indexLabels[1]->setString(fmt::format("Center\nindex={}", listView->getIndex(center)));
                _indexLabels[2]->setString(fmt::format("Bottom index={}", listView->getIndex(bottom)));
            });
        }

        return true;
    }

    return false;
}

float UIListViewTest_Vertical::getItemPositionYInView(ax::ui::Widget* item) const
{
    auto worldPos = item->getParent()->convertToWorldSpaceAR(item->getPosition());
    auto viewPos  = this->_listView->convertToNodeSpaceAR(worldPos);
    return viewPos.y;
}

void UIListViewTest_Vertical::updateItem(int itemID, int templateID)
{
    auto itemTemplate = _listView->getItems().at(templateID);
    Button* btn       = (Button*)itemTemplate->getChildByName("Title Button");
    // If you have  the item ID and templateID, you could fill in the data here
    itemTemplate->setTag(itemID);
    btn->setTitleText(_array.at(itemID));
}

void UIListViewTest_Vertical::update(float dt)
{
    this->_updateTimer += dt;
    if (this->_updateTimer < this->_updateInterval)
    {
        return;
    }

    float totalHeight = _itemTemplateHeight * _totalCount + (_totalCount - 1) * 4;

    auto listViewHeight = _listView->getContentSize().height;

    this->_updateTimer = 0;
    auto isDown        = this->_listView->getInnerContainerPosition().y < this->_lastContentPosY;
    auto items         = _listView->getItems();

    for (int i = 0; i < _spawnCount && i < _totalCount; ++i)
    {
        auto item    = items.at(i);
        auto itemPos = this->getItemPositionYInView(item);
        if (isDown)
        {
            if (itemPos < -_bufferZone && item->getPosition().y + _reuseItemOffset < totalHeight)
            {
                int itemID = item->getTag() - (int)items.size();
                item->setPositionY(item->getPositionY() + _reuseItemOffset);
                AXLOGD("itemPos = {}, itemID = {}, templateID = {}", itemPos, itemID, i);
                this->updateItem(itemID, i);
            }
        }
        else
        {
            if (itemPos > _bufferZone + listViewHeight && item->getPosition().y - _reuseItemOffset >= 0)
            {

                item->setPositionY(item->getPositionY() - _reuseItemOffset);
                int itemID = item->getTag() + (int)items.size();
                AXLOGD("itemPos = {}, itemID = {}, templateID = {}", itemPos, itemID, i);
                this->updateItem(itemID, i);
            }
        }
    }
    // update ListView Items
    this->_lastContentPosY = this->_listView->getInnerContainer()->getPosition().y;
}

void UIListViewTest_Vertical::selectedItemEvent(Object* pSender, ListView::EventType type)
{
    switch (type)
    {
    case ax::ui::ListView::EventType::ON_SELECTED_ITEM_START:
    {
        ListView* listView = static_cast<ListView*>(pSender);
        auto item          = listView->getItem(listView->getCurSelectedIndex());
        AXLOGD("select child start index = {}", item->getTag());
        break;
    }
    case ax::ui::ListView::EventType::ON_SELECTED_ITEM_END:
    {
        ListView* listView = static_cast<ListView*>(pSender);
        auto item          = listView->getItem(listView->getCurSelectedIndex());
        AXLOGD("select child end index = {}", item->getTag());
        break;
    }
    default:
        break;
    }
}

void UIListViewTest_Vertical::selectedItemEventScrollView(Object* pSender, ui::ScrollView::EventType type)
{
    switch (type)
    {
    case ui::ScrollView::EventType::SCROLL_TO_BOTTOM:
        AXLOGD("SCROLL_TO_BOTTOM");
        break;
    case ui::ScrollView::EventType::SCROLL_TO_TOP:
        AXLOGD("SCROLL_TO_TOP");
        break;
    default:
        break;
    }
}

// UIListViewTest_Horizontal

UIListViewTest_Horizontal::UIListViewTest_Horizontal() {}

UIListViewTest_Horizontal::~UIListViewTest_Horizontal() {}

bool UIListViewTest_Horizontal::init()
{
    if (UIScene::init())
    {
        Size widgetSize = _widget->getContentSize();

        _displayValueLabel =
            Text::create("There are 50 items, but we only create 5 templates", "fonts/Marker Felt.ttf", 20);

        _displayValueLabel->setAnchorPoint(Vec2(0.5f, -1.0f));
        _displayValueLabel->setPosition(Vec2(
            widgetSize.width / 2.0f, widgetSize.height / 2.0f + _displayValueLabel->getContentSize().height * 2 + 20));

        _uiLayer->addChild(_displayValueLabel);

        Text* alert = Text::create("ListView horizontal", "fonts/Marker Felt.ttf", 30);
        alert->setColor(Color32(159, 168, 176));
        alert->setPosition(
            Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f - alert->getContentSize().height * 3.075f));
        _uiLayer->addChild(alert);

        Layout* root = static_cast<Layout*>(_uiLayer->getChildByTag(81));

        Layout* background  = static_cast<Layout*>(root->getChildByName("background_Panel"));
        Size backgroundSize = background->getContentSize();

        // create listview  data
        // The data is usually fetch from the server
        for (int i = 0; i < _totalCount; ++i)
        {
            std::string ccstr = fmt::format("listview_item_{}", i);
            _array.emplace_back(ccstr);
        }

        // Create the list view ex
        _listView = ListView::create();
        // set list view ex direction
        _listView->setDirection(ui::ScrollView::Direction::HORIZONTAL);
        _listView->setTouchEnabled(true);
        _listView->setBounceEnabled(true);
        _listView->setBackGroundImage("cocosui/green_edit.png");
        _listView->setBackGroundImageScale9Enabled(true);
        _listView->setContentSize(Size(240.0f, 130.0f));
        _listView->setPosition(Vec2((widgetSize.width - backgroundSize.width) / 2.0f +
                                        (backgroundSize.width - _listView->getContentSize().width) / 2.0f,
                                    (widgetSize.height - backgroundSize.height) / 2.0f +
                                        (backgroundSize.height - _listView->getContentSize().height) / 2.0f));
        _listView->addEventListener(
            (ui::ListView::ccListViewCallback)AX_CALLBACK_2(UIListViewTest_Horizontal::selectedItemEvent, this));
        _listView->setScrollBarPositionFromCorner(Vec2(7, 7));
        _uiLayer->addChild(_listView);

        // create model
        Button* default_button = Button::create("cocosui/backtotoppressed.png", "cocosui/backtotopnormal.png");
        default_button->setName("Title Button");

        Layout* default_item = Layout::create();
        default_item->setTouchEnabled(true);
        default_item->setContentSize(default_button->getContentSize());
        default_button->setPosition(
            Vec2(default_item->getContentSize().width / 2.0f, default_item->getContentSize().height / 2.0f));
        default_item->addChild(default_button);

        // set model
        _listView->setItemModel(default_item);

        // set all items layout gravity
        _listView->setGravity(ListView::Gravity::CENTER_VERTICAL);

        // initial the data
        for (int i = 0; i < _totalCount; ++i)
        {
            if (i < _spawnCount)
            {
                Widget* item = default_item->clone();
                item->setTag(i);
                Button* btn = (Button*)item->getChildByName("Title Button");
                btn->setTitleText(_array.at(i));
                _listView->pushBackCustomItem(item);
            }
        }

        // set items margin
        float spacing = 4;
        _listView->setItemsMargin(spacing);
        _itemTemplateWidth     = default_item->getContentSize().width;
        this->_reuseItemOffset = (_itemTemplateWidth + spacing) * _spawnCount;

        _listView->forceDoLayout();
        float totalWidth = _itemTemplateWidth * _totalCount + (_totalCount - 1) * 4;
        _listView->getInnerContainer()->setContentSize(Size(totalWidth, _listView->getInnerContainerSize().height));

        // update listview data
        this->scheduleUpdate();

        return true;
    }

    return false;
}

float UIListViewTest_Horizontal::getItemPositionXInView(ax::ui::Widget* item) const
{
    auto worldPos = item->getParent()->convertToWorldSpaceAR(item->getPosition());
    auto viewPos  = this->_listView->convertToNodeSpaceAR(worldPos);
    return viewPos.x;
}

void UIListViewTest_Horizontal::updateItem(int itemID, int templateID)
{
    auto itemTemplate = _listView->getItems().at(templateID);
    Button* btn       = (Button*)itemTemplate->getChildByName("Title Button");
    // If you have  the item ID and templateID, you could fill in the data here
    itemTemplate->setTag(itemID);
    btn->setTitleText(_array.at(itemID));
}

void UIListViewTest_Horizontal::update(float dt)
{
    this->_updateTimer += dt;
    if (this->_updateTimer < this->_updateInterval)
    {
        return;
    }
    float totalWidth = _itemTemplateWidth * _totalCount + (_totalCount - 1) * 4;

    this->_updateTimer = 0;
    auto isRight       = this->_listView->getInnerContainer()->getPosition().x < this->_lastContentPosX;
    auto items         = _listView->getItems();

    for (int i = 0; i < _spawnCount && i < _totalCount; ++i)
    {
        auto item    = items.at(i);
        auto itemPos = this->getItemPositionXInView(item);
        if (isRight)
        {
            if (itemPos < -_bufferZone && item->getPosition().x + _reuseItemOffset < totalWidth)
            {
                int itemID = item->getTag() + (int)items.size();
                item->setPositionX(item->getPositionX() + _reuseItemOffset);
                AXLOGD("itemPos = {}, itemID = {}, templateID = {}", itemPos, itemID, i);
                this->updateItem(itemID, i);
            }
        }
        else
        {
            if (itemPos > _bufferZone + _listView->getContentSize().width &&
                item->getPosition().x - _reuseItemOffset >= 0)
            {

                item->setPositionX(item->getPositionX() - _reuseItemOffset);
                int itemID = item->getTag() - (int)items.size();
                AXLOGD("itemPos = {}, itemID = {}, templateID = {}", itemPos, itemID, i);
                this->updateItem(itemID, i);
            }
        }
    }
    // update ListView Items
    this->_lastContentPosX = this->_listView->getInnerContainer()->getPosition().x;
}

void UIListViewTest_Horizontal::selectedItemEvent(Object* pSender, ListView::EventType type)
{
    switch (type)
    {
    case ax::ui::ListView::EventType::ON_SELECTED_ITEM_START:
    {
        ListView* listView = static_cast<ListView*>(pSender);
        auto item          = listView->getItem(listView->getCurSelectedIndex());
        AXLOGD("select child start index = {}", item->getTag());
        break;
    }
    case ax::ui::ListView::EventType::ON_SELECTED_ITEM_END:
    {
        ListView* listView = static_cast<ListView*>(pSender);
        auto item          = listView->getItem(listView->getCurSelectedIndex());
        AXLOGD("select child end index = {}", item->getTag());
        break;
    }
    default:
        break;
    }
}

bool Issue12692::init()
{
    if (UIScene::init())
    {
        Size widgetSize = _widget->getContentSize();

        auto label = Text::create("Issue 12692", "fonts/Marker Felt.ttf", 32);
        label->setName("Text Title");
        label->setAnchorPoint(Vec2(0.5f, -1.0f));
        label->setPosition(
            Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + label->getContentSize().height * 1.5f));
        _uiLayer->addChild(label);

        Text* alert = Text::create("ListView in ListView enable Scissor Clipping", "fonts/Marker Felt.ttf", 20);
        alert->setName("Text Alert");
        alert->setColor(Color32(159, 168, 176));
        alert->setPosition(
            Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f - alert->getContentSize().height * 3.075f));
        _uiLayer->addChild(alert);

        Layout* root = static_cast<Layout*>(_uiLayer->getChildByTag(81));

        Layout* background  = dynamic_cast<Layout*>(root->getChildByName("background_Panel"));
        Size backgroundSize = background->getContentSize();

        // Create the list view ex
        ListView* listView = ListView::create();
        // set list view ex direction
        listView->setDirection(ui::ScrollView::Direction::VERTICAL);
        listView->setBounceEnabled(true);
        listView->setBackGroundImage("cocosui/green_edit.png");
        listView->setBackGroundImageScale9Enabled(true);
        listView->setContentSize(Size(240.0f, 130.0f));
        listView->setPosition(Vec2((widgetSize.width - backgroundSize.width) / 2.0f +
                                       (backgroundSize.width - listView->getContentSize().width) / 2.0f,
                                   (widgetSize.height - backgroundSize.height) / 2.0f +
                                       (backgroundSize.height - listView->getContentSize().height) / 2.0f));
        listView->setScrollBarPositionFromCorner(Vec2(7, 7));
        listView->setClippingEnabled(true);
        listView->setClippingType(ui::Layout::ClippingType::SCISSOR);
        listView->setName("listview1");
        _uiLayer->addChild(listView);

        auto list2 = ListView::create();
        list2->setDirection(ui::ScrollView::Direction::VERTICAL);
        list2->setBounceEnabled(true);
        list2->setBackGroundImage("cocosui/green_edit.png");
        list2->setBackGroundImageScale9Enabled(true);
        list2->setContentSize(Size(240, 65));
        list2->setClippingEnabled(true);
        list2->setClippingType(ui::Layout::ClippingType::SCISSOR);
        list2->setName("listview2");
        listView->insertCustomItem(list2, 0);

        {
            Button* default_button = Button::create("cocosui/backtotoppressed.png", "cocosui/backtotopnormal.png");
            default_button->setName("Title Button");

            Layout* default_item = Layout::create();
            default_item->setTouchEnabled(true);
            default_item->setContentSize(default_button->getContentSize());
            default_button->setPosition(
                Vec2(default_item->getContentSize().width / 2.0f, default_item->getContentSize().height / 2.0f));
            default_item->addChild(default_button);

            // set model
            listView->setItemModel(default_item);
            listView->pushBackDefaultItem();
            listView->pushBackDefaultItem();
            listView->pushBackDefaultItem();
        }
        {
            Button* default_button = Button::create("cocosui/backtotoppressed.png", "cocosui/backtotopnormal.png");
            default_button->setName("Title Button 2");

            Layout* default_item = Layout::create();
            default_item->setTouchEnabled(true);
            default_item->setContentSize(default_button->getContentSize());
            default_button->setPosition(
                Vec2(default_item->getContentSize().width / 2.0f, default_item->getContentSize().height / 2.0f));
            default_item->addChild(default_button);

            // set model
            list2->setItemModel(default_item);
            list2->pushBackDefaultItem();
            list2->pushBackDefaultItem();
            list2->pushBackDefaultItem();
        }
        return true;
    }

    return false;
}

bool Issue8316::init()
{
    if (UIScene::init())
    {
        Size widgetSize = _widget->getContentSize();

        auto label = Text::create("Issue 8316", "fonts/Marker Felt.ttf", 32);
        label->setAnchorPoint(Vec2(0.5f, -1.0f));
        label->setPosition(
            Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f + label->getContentSize().height * 1.5f));
        _uiLayer->addChild(label);

        Text* alert = Text::create("Can not scroll list view", "fonts/Marker Felt.ttf", 20);
        alert->setColor(Color32(159, 168, 176));
        alert->setPosition(
            Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f - alert->getContentSize().height * 3.075f));
        _uiLayer->addChild(alert);

        Layout* root = static_cast<Layout*>(_uiLayer->getChildByTag(81));

        Layout* background  = dynamic_cast<Layout*>(root->getChildByName("background_Panel"));
        Size backgroundSize = background->getContentSize();

        // Create the list view ex
        ListView* listView = ListView::create();
        // set list view ex direction
        listView->setDirection(ui::ScrollView::Direction::VERTICAL);
        listView->setBounceEnabled(true);
        listView->setTouchEnabled(false);
        listView->setBackGroundImage("cocosui/green_edit.png");
        listView->setBackGroundImageScale9Enabled(true);
        listView->setContentSize(Size(240.0f, 130.0f));
        listView->setPosition(Vec2((widgetSize.width - backgroundSize.width) / 2.0f +
                                       (backgroundSize.width - listView->getContentSize().width) / 2.0f,
                                   (widgetSize.height - backgroundSize.height) / 2.0f +
                                       (backgroundSize.height - listView->getContentSize().height) / 2.0f));
        listView->setScrollBarPositionFromCorner(Vec2(7, 7));
        listView->setClippingEnabled(true);
        listView->setClippingType(ui::Layout::ClippingType::SCISSOR);
        listView->setName("listview1");

        {
            Button* default_button = Button::create("cocosui/backtotoppressed.png", "cocosui/backtotopnormal.png");
            default_button->setName("Title Button");

            Layout* default_item = Layout::create();
            default_item->setTouchEnabled(true);
            default_item->setContentSize(default_button->getContentSize());
            default_button->setPosition(
                Vec2(default_item->getContentSize().width / 2.0f, default_item->getContentSize().height / 2.0f));
            default_item->addChild(default_button);

            // set model
            listView->setItemModel(default_item);
            listView->pushBackDefaultItem();
            listView->pushBackDefaultItem();
            listView->pushBackDefaultItem();
        }

        _uiLayer->addChild(listView);

        return true;
    }

    return false;
}

// UIListViewTest_ScrollToItem
bool UIListViewTest_ScrollToItem::init()
{
    if (!UIScene::init())
    {
        return false;
    }

    Size layerSize = _uiLayer->getContentSize();

    static int NUMBER_OF_ITEMS = 31;
    _nextIndex                 = 0;
    _titleLabel                = Text::create("Scroll to item", "fonts/Marker Felt.ttf", 32);
    _titleLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _titleLabel->setPosition(Vec2(layerSize / 2) + Vec2(0.0f, _titleLabel->getContentSize().height * 3.15f));
    _uiLayer->addChild(_titleLabel, 3);

    // Create the list view
    _listView = ListView::create();
    _listView->setDirection(getListViewDirection());
    _listView->setBounceEnabled(true);
    _listView->setBackGroundImage("cocosui/green_edit.png");
    _listView->setBackGroundImageScale9Enabled(true);
    _listView->setContentSize(layerSize / 2);
    _listView->setScrollBarPositionFromCorner(Vec2(7, 7));
    _listView->setItemsMargin(2.0f);
    _listView->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _listView->setPosition(layerSize / 2);
    _uiLayer->addChild(_listView);

    // Guide line for center align
    {
        DrawNode* pNode = DrawNode::create();
        Vec2 center     = layerSize / 2;
        if (getListViewDirection() == ScrollView::Direction::HORIZONTAL)
        {
            float halfY = 110;
            pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color(0, 0, 0, 1),
                            2.0f);
        }
        else
        {
            float halfX = 150;
            pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color(0, 0, 0, 1),
                            2.0f);
        }
        _uiLayer->addChild(pNode);
    }

    // Button
    auto pButton = Button::create("cocosui/backtotoppressed.png", "cocosui/backtotopnormal.png");
    pButton->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    pButton->setScale(0.8f);
    pButton->setPosition(Vec2(layerSize / 2) + Vec2(120.0f, -60.0f));
    pButton->setTitleText(fmt::format("Go to '{}'", _nextIndex));
    pButton->addClickEventListener([this, pButton](Object*) {
        _listView->scrollToItem(_nextIndex, Vec2::ANCHOR_MIDDLE, Vec2::ANCHOR_MIDDLE);
        _nextIndex = (_nextIndex + (NUMBER_OF_ITEMS / 2)) % NUMBER_OF_ITEMS;
        pButton->setTitleText(fmt::format("Go to '{}'", _nextIndex));
    });
    _uiLayer->addChild(pButton);

    // Add list items
    static const Size BUTTON_SIZE(50, 40);
    for (int i = 0; i < NUMBER_OF_ITEMS; ++i)
    {
        auto pButton = Button::create("cocosui/button.png", "cocosui/buttonHighlighted.png");
        pButton->setContentSize(BUTTON_SIZE);
        pButton->setScale9Enabled(true);
        pButton->setTitleText(fmt::format("Button-{}", i));
        _listView->pushBackCustomItem(pButton);
    }
    return true;
}

// UIListViewTest_Magnetic
bool UIListViewTest_Magnetic::init()
{
    if (!UIScene::init())
    {
        return false;
    }

    Size layerSize = _uiLayer->getContentSize();

    _titleLabel = Text::create("Magnetic scroll", "fonts/Marker Felt.ttf", 32);
    _titleLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _titleLabel->setPosition(Vec2(layerSize / 2) + Vec2(0.0f, _titleLabel->getContentSize().height * 3.15f));
    _uiLayer->addChild(_titleLabel, 3);

    // Create the list view
    _listView = ListView::create();
    _listView->setDirection(getListViewDirection());
    _listView->setBounceEnabled(true);
    _listView->setBackGroundImage("cocosui/green_edit.png");
    _listView->setBackGroundImageScale9Enabled(true);
    _listView->setContentSize(layerSize / 2);
    _listView->setScrollBarPositionFromCorner(Vec2(7, 7));
    _listView->setItemsMargin(2.0f);
    _listView->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _listView->setPosition(layerSize / 2);
    _uiLayer->addChild(_listView);

    // Guide line for center align
    {
        DrawNode* pNode = DrawNode::create();
        Vec2 center     = layerSize / 2;
        if (getListViewDirection() == ScrollView::Direction::HORIZONTAL)
        {
            float halfY = 110;
            pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color(0, 0, 0, 1),
                            2.0f);
        }
        else
        {
            float halfX = 150;
            pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color(0, 0, 0, 1),
                            2.0f);
        }
        _uiLayer->addChild(pNode);
    }

    // Show the indexes of items on each boundary.
    {
        for (int i = 0; i < 5; ++i)
        {
            _indexLabels[i] = Text::create(" ", "fonts/Marker Felt.ttf", 12);
            _indexLabels[i]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
            _uiLayer->addChild(_indexLabels[i]);
        }
        float deltaX = 145, deltaY = 90;
        _indexLabels[0]->setPosition(_uiLayer->getContentSize() / 2 + Size(-deltaX, 0.0f));   // left
        _indexLabels[1]->setPosition(_uiLayer->getContentSize() / 2 + Size(deltaX, 0.0f));    // right
        _indexLabels[2]->setPosition(_uiLayer->getContentSize() / 2 + Size(0.0f, deltaY));    // top
        _indexLabels[3]->setPosition(_uiLayer->getContentSize() / 2 + Size(0.0f, -deltaY));   // bottom
        _indexLabels[4]->setPosition(_uiLayer->getContentSize() / 2 + Size(deltaX, deltaY));  // center

        // Callback
        _listView->ScrollView::addEventListener([this](Object* ref, ScrollView::EventType eventType) {
            ListView* listView = dynamic_cast<ListView*>(ref);
            if (listView == nullptr || eventType != ScrollView::EventType::CONTAINER_MOVED)
            {
                return;
            }
            auto left   = listView->getLeftmostItemInCurrentView();
            auto right  = listView->getRightmostItemInCurrentView();
            auto top    = listView->getTopmostItemInCurrentView();
            auto bottom = listView->getBottommostItemInCurrentView();
            auto center = listView->getCenterItemInCurrentView();

            _indexLabels[0]->setString(fmt::format("Left\nindex={}", listView->getIndex(left)));
            _indexLabels[1]->setString(fmt::format("RIght\nindex={}", listView->getIndex(right)));
            _indexLabels[2]->setString(fmt::format("Top index={}", listView->getIndex(top)));
            _indexLabels[3]->setString(fmt::format("Bottom index={}", listView->getIndex(bottom)));
            _indexLabels[4]->setString(fmt::format("Center\nindex={}", listView->getIndex(center)));
        });
    }

    // Initial magnetic type
    _listView->setMagneticType(ListView::MagneticType::NONE);
    _titleLabel->setString("MagneticType - NONE");

    // Magnetic change button
    auto pButton = Button::create("cocosui/backtotoppressed.png", "cocosui/backtotopnormal.png");
    pButton->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    pButton->setScale(0.8f);
    pButton->setPosition(Vec2(layerSize / 2) + Vec2(130.0f, -60.0f));
    pButton->setTitleText("Next Magnetic");
    pButton->addClickEventListener([this](Object*) {
        ListView::MagneticType eCurrentType = _listView->getMagneticType();
        ListView::MagneticType eNextType    = ListView::MagneticType::NONE;
        std::string sString;
        if (eCurrentType == ListView::MagneticType::NONE)
        {
            eNextType = ListView::MagneticType::CENTER;
            sString   = "CENTER";
        }
        else if (eCurrentType == ListView::MagneticType::CENTER)
        {
            eNextType = ListView::MagneticType::BOTH_END;
            sString   = "BOTH_END";
        }
        else if (eCurrentType == ListView::MagneticType::BOTH_END)
        {
            if (getListViewDirection() == ScrollView::Direction::HORIZONTAL)
            {
                eNextType = ListView::MagneticType::LEFT;
                sString   = "LEFT";
            }
            else
            {
                eNextType = ListView::MagneticType::TOP;
                sString   = "TOP";
            }
        }
        else if (eCurrentType == ListView::MagneticType::LEFT)
        {
            eNextType = ListView::MagneticType::RIGHT;
            sString   = "RIGHT";
        }
        else if (eCurrentType == ListView::MagneticType::RIGHT)
        {
            eNextType = ListView::MagneticType::NONE;
            sString   = "NONE";
        }
        else if (eCurrentType == ListView::MagneticType::TOP)
        {
            eNextType = ListView::MagneticType::BOTTOM;
            sString   = "BOTTOM";
        }
        else if (eCurrentType == ListView::MagneticType::BOTTOM)
        {
            eNextType = ListView::MagneticType::NONE;
            sString   = "NONE";
        }
        _listView->setMagneticType(eNextType);
        _titleLabel->setString(fmt::format("MagneticType - {}", sString));
    });
    _uiLayer->addChild(pButton);

    // Add list items
    static const Size BUTTON_SIZE(100, 70);
    for (int i = 0; i < 40; ++i)
    {
        auto pButton = Button::create("cocosui/button.png", "cocosui/buttonHighlighted.png");
        pButton->setContentSize(BUTTON_SIZE);
        pButton->setScale9Enabled(true);
        pButton->setTitleText(fmt::format("Button-{}", i));
        _listView->pushBackCustomItem(pButton);
    }
    return true;
}

// UIListViewTest_Padding
bool UIListViewTest_Padding::init()
{
    if (!UIScene::init())
    {
        return false;
    }

    Size layerSize = _uiLayer->getContentSize();

    _titleLabel = Text::create("Set Padding", "fonts/Marker Felt.ttf", 32);
    _titleLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _titleLabel->setPosition(Vec2(layerSize / 2) + Vec2(0.0f, _titleLabel->getContentSize().height * 3.15f));
    _uiLayer->addChild(_titleLabel, 3);

    // Create the list view
    _listView = ListView::create();
    _listView->setDirection(getListViewDirection());
    _listView->setBounceEnabled(true);
    _listView->setBackGroundImage("cocosui/green_edit.png");
    _listView->setBackGroundImageScale9Enabled(true);
    _listView->setContentSize(layerSize / 2);
    _listView->setScrollBarPositionFromCorner(Vec2(7, 7));
    _listView->setItemsMargin(2.0f);
    _listView->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _listView->setGravity(ListView::Gravity::TOP);
    _listView->setPosition(layerSize / 2);
    _uiLayer->addChild(_listView);

    // Guide line for center align
    {
        DrawNode* pNode = DrawNode::create();
        Vec2 center     = layerSize / 2;
        if (getListViewDirection() == ScrollView::Direction::HORIZONTAL)
        {
            float halfY = 110;
            pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color(0, 0, 0, 1),
                            2.0f);
        }
        else
        {
            float halfX = 150;
            pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color(0, 0, 0, 1),
                            2.0f);
        }
        _uiLayer->addChild(pNode);
    }

    // Slider for setting padding
    {
        auto title = Text::create("Padding", "fonts/Marker Felt.ttf", 14);
        title->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
        title->setPosition(Vec2(30.0f, 170.0f));
        _uiLayer->addChild(title);
        for (int i = 0; i < 4; ++i)
        {
            auto slider = Slider::create();
            slider->loadBarTexture("cocosui/sliderTrack.png");
            slider->loadSlidBallTextures("cocosui/sliderThumb.png", "cocosui/sliderThumb.png", "");
            slider->loadProgressBarTexture("cocosui/sliderProgress.png");
            slider->setScale9Enabled(true);
            slider->setCapInsets(Rect(0.0f, 0.0f, 0.0f, 0.0f));
            slider->setContentSize(Size(30.0f, 10.0f));
            slider->setPosition(Vec2(60.0f, 150.0f - (25 * i)));
            slider->addEventListener(AX_CALLBACK_2(UIListViewTest_Padding::sliderEvent, this));
            slider->setTag(i);
            _uiLayer->addChild(slider);

            std::string str;
            if (i == 0)
                str = "Left";
            else if (i == 1)
                str = "Top";
            else if (i == 2)
                str = "Right";
            else if (i == 3)
                str = "Bottom";

            // Show title of slider
            {
                auto text = Text::create(str, "fonts/Marker Felt.ttf", 12);
                text->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
                text->setPosition(Vec2(3, 150 - (25 * i)));
                _uiLayer->addChild(text);
            }
            // Show value of paddings
            {
                auto text = Text::create(str + "\nPadding=0", "fonts/Marker Felt.ttf", 12);
                text->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
                text->setPosition(Vec2(layerSize.width - 65, 200.0f - (40 * i)));
                _uiLayer->addChild(text);

                _paddingLabels[i] = text;
            }
        }
    }

    // Show the indexes of items on each boundary.
    {
        for (int i = 0; i < 5; ++i)
        {
            _indexLabels[i] = Text::create(" ", "fonts/Marker Felt.ttf", 12);
            _indexLabels[i]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
            _uiLayer->addChild(_indexLabels[i]);
        }
        float deltaX = 145, deltaY = 90;
        _indexLabels[0]->setPosition(_uiLayer->getContentSize() / 2 + Size(-deltaX, 0.0f));   // left
        _indexLabels[1]->setPosition(_uiLayer->getContentSize() / 2 + Size(deltaX, 0.0f));    // right
        _indexLabels[2]->setPosition(_uiLayer->getContentSize() / 2 + Size(0.0f, deltaY));    // top
        _indexLabels[3]->setPosition(_uiLayer->getContentSize() / 2 + Size(0.0f, -deltaY));   // bottom
        _indexLabels[4]->setPosition(_uiLayer->getContentSize() / 2 + Size(deltaX, deltaY));  // center

        // Callback
        _listView->ScrollView::addEventListener([this](Object* ref, ScrollView::EventType eventType) {
            ListView* listView = dynamic_cast<ListView*>(ref);
            if (listView == nullptr || eventType != ScrollView::EventType::CONTAINER_MOVED)
            {
                return;
            }
            auto left   = listView->getLeftmostItemInCurrentView();
            auto right  = listView->getRightmostItemInCurrentView();
            auto top    = listView->getTopmostItemInCurrentView();
            auto bottom = listView->getBottommostItemInCurrentView();
            auto center = listView->getCenterItemInCurrentView();

            _indexLabels[0]->setString(fmt::format("Left\nindex={}", listView->getIndex(left)));
            _indexLabels[1]->setString(fmt::format("RIght\nindex={}", listView->getIndex(right)));
            _indexLabels[2]->setString(fmt::format("Top index={}", listView->getIndex(top)));
            _indexLabels[3]->setString(fmt::format("Bottom index={}", listView->getIndex(bottom)));
            _indexLabels[4]->setString(fmt::format("Center\nindex={}", listView->getIndex(center)));
        });
    }

    // Add list items
    static const Size BUTTON_SIZE(100, 70);
    for (int i = 0; i < 40; ++i)
    {
        auto pButton = Button::create("cocosui/button.png", "cocosui/buttonHighlighted.png");
        pButton->setContentSize(BUTTON_SIZE);
        pButton->setScale9Enabled(true);
        pButton->setTitleText(fmt::format("Button-{}", i));
        _listView->pushBackCustomItem(pButton);
    }
    return true;
}

void UIListViewTest_Padding::sliderEvent(Object* pSender, Slider::EventType type)
{
    if (type == Slider::EventType::ON_PERCENTAGE_CHANGED)
    {
        auto slider = dynamic_cast<Slider*>(pSender);
        // left
        if (slider && slider->getTag() == 0)
        {
            int left = slider->getPercent() / 100.f * 50.f;
            AXLOGD("Left Padding: {}", left);
            _listView->setLeftPadding(left);
            _paddingLabels[0]->setString(fmt::format("Left\nPadding={}", left));
        }
        // top
        if (slider && slider->getTag() == 1)
        {
            int top = slider->getPercent() / 100.f * 50.f;
            AXLOGD("Top Padding: {}", top);
            _listView->setTopPadding(top);
            _paddingLabels[1]->setString(fmt::format("Top\nPadding={}", top));
        }
        // right
        if (slider && slider->getTag() == 2)
        {
            int right = slider->getPercent() / 100.f * 50.f;
            AXLOGD("Right Padding: {}", right);
            _listView->setRightPadding(right);
            _paddingLabels[2]->setString(fmt::format("Right\nPadding={}", right));
        }
        // bottom
        if (slider && slider->getTag() == 3)
        {
            int bottom = slider->getPercent() / 100.f * 50.f;
            AXLOGD("Bottom Padding: {}", bottom);
            _listView->setBottomPadding(bottom);
            _paddingLabels[3]->setString(fmt::format("Bottom\nPadding={}", bottom));
        }
    }
}
